module CyclomaticComplexity

import General;
import LinesOfCode;
import IO;
import Relation;
import Set;
import util::Math;
import lang::java::jdt::JDT;
import lang::java::jdt::JavaADT;
import List;
import vis::Figure;
import vis::Render;
import vis::KeySym;
import util::Editors;

Figure testpack;

public rel[loc, rel[int, int]] ExtractInformation(list[loc] files)
{
	return aux = {<f, ExtractFileInformation(f)> | loc f <- files};
}

public rel[int, int] ExtractFileInformation(loc file)
{
	fm = extractClass(file);
	return aux = {<CyclomaticComplexity(body),  LinesOfCode(location)> | <method, body> <- fm@methodBodies, <location, method1> <- fm@methods, method == method1};
}

public list[tuple[int, int,loc]] ExtractMethodInformation(loc file)
{
	fm = extractClass(file);
	return aux = [<CyclomaticComplexity(body),  LinesOfCode(location), location > | <method, body> <- fm@methodBodies, <location, method1> <- fm@methods, method == method1];
}

public int CyclomaticComplexity(AstNode body)
{
	int cc = 1;

	visit(body)
	{
		case doStatement(_, _) : cc += 1;
		case enhancedForStatement(_, _, _) : cc += 1;
		case forStatement(_, _, _, _) : cc += 1;
		case ifStatement(_, _, _) : cc += 1;
		case switchCase(_, _) : cc += 1;										
		case catchClause(_, _) : cc += 1;
		case whileStatement(_, _) : cc += 1;
	}

	return cc;
}

public void Visualize(str project)
{
	list[loc] files = GetAllJavaFiles(project);
	testpack = pack(GetBoxes(files), std(gap(10)));
	
	render(testpack);
}

public Figures GetBoxes(list[loc] files)
{
	Figures figures = []; 
	
	for(tuple[loc, rel[int, int]]  fileInformation <- ExtractInformation(files))
	{
		int lowRiskLines = 0, moderateRiskLines = 0, highRiskLines = 0, veryHighRiskLines = 0, totalRiskLines = 0, methods = 0;	
		for(information <- fileInformation[1])
		{
			methods +=1;
			totalRiskLines += information[1];
			
			if(information[0] < 10) 
				 lowRiskLines += information[1];
			else if (information[0] < 20) 
				moderateRiskLines += information[1];
			else if (information[0] < 50) 
				highRiskLines += information[1];
			else 
				veryHighRiskLines += information[1];
		}
				 
		if(totalRiskLines > 0)
		{
			oldLoc = fileInformation[0];
			
			int lowRisk = percent(lowRiskLines, totalRiskLines);
			int moderateRisk = percent(moderateRiskLines, totalRiskLines);
			int highRisk = percent(highRiskLines, totalRiskLines);
			int veryHighRisk = percent(veryHighRiskLines, totalRiskLines);
		
			str operator = "";
			real  percentage;
			if(moderateRisk <= 25 && highRisk == 0 && veryHighRisk == 0) {
				operator = "++";
				percentage = 0.0;
			}
			else if(moderateRisk <= 30 && highRisk <= 5 && veryHighRisk == 0) {
				operator = "+";
				percentage = 0.25;
			}
			else if(moderateRisk <= 40 && highRisk <= 10 && veryHighRisk == 0) {
				operator = "o";
				percentage = 0.50;
			}
			else if(moderateRisk <= 50 && highRisk <= 15 && veryHighRisk <= 5) {	
				operator = "-";
				percentage = 0.75;
			}
			else {
				operator = "--";
				percentage = 1.0;
			}	
			
			figures += box([size(methods * 5, totalRiskLines), 
						fillColor(interpolateColor(color("White"), color("Red"), percentage)), 
						popup(fileInformation[0], operator, totalRiskLines, methods),
						onMouseDown(bool (int butnr, map[KeyModifier,bool] modifiers) {
							render(GetOutLine(oldLoc));
						}
					)
				]
			);
		}
	}	
	
	return figures;
}

public FProperty popup(loc location, str risk, int linesOfCode, int methods)
{	
	list[Figure] vcatList = [text("<location>"), text("<risk> CC"), text("<linesOfCode> lines of code")];
	
	if(methods != 0)
		vcatList += text("<methods> methods");
	 	
 	return mouseOver(
 			box(
 				vcat(vcatList), 
 				fillColor("lightyellow"),
               	grow(1.3), 
               	right(),
               	resizable(false)
           	)
       	);
}

public Figure GetOutLine(loc file)
{
	list[tuple[int,int,loc]] methods = reverse(ExtractMethodInformation(file));
	Figures decor = []; 
	
	int width = 0;
	real percentage = 0.0;
	str risk = "";
	for(method <- methods)
	{
		width = method[1];
		cc = method[0];
		
		if(cc < 10)
		{ 
			percentage = 0.0;
			risk = "++";
		}
		else if (cc < 20) 
		{
			percentage = 0.25;
			risk = "+";
		}		
		else if(cc < 30)
		{ 
			percentage = 0.5;
			risk = "0";
		}
		else if(cc < 40 )
		{
			percentage = 0.75;
			risk = "-";
		}
		else
		{		
			percentage = 1.0;
			risk = "--";
		}
		
		if (width < 3)
			width = 3;	
			
		oldMethod = method[2];
		
		decor += box(
			size(width, 150),
			fillColor(interpolateColor(color("White"), color("Red"), percentage)),
			lineWidth(1), lineColor("Black"),
			popup(method[2], risk, method[1], 0),
			onMouseDown(bool (int butnr, map[KeyModifier, bool] modifiers) {
				edit(oldMethod);
			}),
			onKeyDown(bool (KeySym key, map[KeyModifier, bool] modifiers) {
				render(testpack);
			})
		);	
	}	
	
	return hvcat(decor, ialign(0.0), resizable(true, true));
}

//public void GetOutLineOld(loc file)
//{
//	list[tuple[int, int,loc]] methods = reverse(ExtractMethodInformation(file));
//	str fillType = "highlight";
//	int width = 0, cc = 0, color = -1, lines = 0;
//	list[LineDecoration] decor = [];
//	 
//	for(method <- methods)
//	{
//		width = method[1];
//		cc = method[0];
//		
//		if(cc < 10) 
//			color = 1;
//		else if (cc < 20) 
//			color = 2;		
//		else if (cc < 50) 
//			color = 8;
//		else 
//			fillType = "error";
//			
//		for(int i <- [0 .. width - 1])
//		{
//			if(fillType == "highlight")
//			{
//				decor += highlight(lines, "a", color);
//				lines += 1;
//			}
//			else
//			{
//				decor += error(lines, "b");
//				lines += 1;
//			}
//		}
//		
//		fillType = "highlight";	
//		lines += 2;
//	}	
//	
// 	render(outline(decor, lines, size(125, lines), fillColor("lightGrey")));	
//}